package org.diy.traffic.updates;

import java.util.Date;
import java.util.HashMap;

import org.diy.traffic.sources.TrafficSource;

/**
 * Represents a traffic update
 * @author Dan Greenblatt
 *
 */
public class TrafficUpdate {
	
	/**
	 * A counter which keeps tracks of how many traffic updates are generated; can be used as a unique id
	 */
	public static int uniqueUpdateID = 0;
	
	/**
	 * The uid for this TrafficUpdate instance. Essentially a serial number.
	 */
	protected int updateUID;
	
	/**
	 * A timestamp (time in milliseconds since the epoch) representing when this TrafficUpdate was created
	 */
	private long timestamp;
	
	/**
	 * A reference to the source from which this TrafficUpdate was generated
	 */
	private TrafficSource source;
	
	/**
	 * A container which could hold arbitrary information pertinent to this TrafficUpdate. Maps a unique
	 * identifier to an Object 
	 */
	private HashMap<String, Object> data;
	
	
	/**
	 * The actual content of this traffic update
	 */
	private String message;
	
	/**
	 * Constructor for a TrafficUpdate
	 * @param source The source which generated this TrafficUpdate
	 * @param t A timestamp (in milliseconds since the epoch) when this TrafficUpdate was generated
	 * @param msg The content of this TrafficUpdate (i.e. traffic information)
	 */
	public TrafficUpdate(TrafficSource source, long t, String msg) {
		this.source    = source;
		this.timestamp = t;
		this.message   = msg;
		
		// update the unique identifier
		this.updateUID = ++TrafficUpdate.uniqueUpdateID;
		
		// initialize the data map to hold any arbitrary Objects associated with this TrafficUpdate
		data = new HashMap<String, Object>();
	}
	
	/**
	 * Add an object to the data HashMap for this TrafficUpdate
	 * @param s The key to represent the Object being added
	 * @param o An arbitrary object to add to this TrafficUpdate
	 */
	public void addData(String s, Object o) {
		data.put(s, o);
	}
	
	/**
	 * Get the unique id for this TrafficUpdate
	 * @return the unique id (serial number) for this TrafficUpdate
	 */
	public int getUniqueID() {
		return this.updateUID;
	}
	
	/**
	 * Get an object from this TrafficUpdate's data map 
	 * @param s the key for the Object
	 * @return the Object which is the value of key
	 * @throws UnknownDataException if the key doesn't exist in this TrafficUpdate's data map
	 */
	public Object getData(String s) throws UnknownDataException {
		
		Object result = data.get(s);
		if (result == null) {
			throw new UnknownDataException(String.format("Can't find data for key %s in this update (%s)", s, this));
		} else {
			return result;
		}
	}
	
	/**
	 * Get the timestamp for this TrafficUpdate
	 * @return A long (time in milliseconds since the epoch) representing the time when this TrafficUpdate was created
	 */
	public long getTimestamp() {
		return this.timestamp;
	}
	
	/**
	 * Get the source from which this TrafficUpdate was generated
	 * @return the source from which this TrafficUpdate was generated
	 */
	public TrafficSource getSource() {
		return this.source;
	}
	
	/**
	 * Get the message (i.e. traffic information) from this TrafficUpdate
	 * @return the traffic information from this Traffic update
 	 */
	public String getMessage() {
		return this.message;
	}
	
	/**
	 * A string representation of this object
	 */
	@Override
	public String toString() {
		return "[" + this.source + "@" + new Date(this.timestamp) + "]: " + this.getMessage();
	}
	
	/**
	 * Override the equals operator which makes it easier to compare TrafficUpdate objects. TrafficUpdates
	 * are compared for equality by the contents of their message
	 */
	@Override
	public boolean equals (Object arg) {
		if ( (arg != null) && (arg instanceof TrafficUpdate)) {
			if (this == arg) {
				return true;
			} else {
				if (this.message.equals( ((TrafficUpdate)arg).getMessage() )) {
					return true;
				} else
					return false;
			} 
		} else
			return false;
	}
	
	/**
	 * Exception which is raised when this TrafficUpdate does not contain a key requested from
	 * the data map
	 * @author Dan Greenblatt
	 *
	 */
	public class UnknownDataException extends Exception {		
		public UnknownDataException(String s) {super(s);}
	}
}
